Go / No-Go Preparations: Cognitive Control and Motivated Reasoning

Data cleaning, data checks, and some preparations for further analyses specific for the Go / No-Go task.
# use groundhog to make code maximally reproducible
if (!require("groundhog", quietly = TRUE)) {
  install.packages("groundhog")
}
library("groundhog")

# use groundhog to install and load packages
pkgs <- c("here",         # System path management
          "tidyverse",    # ggplot, dplyr, %>%, and friends
          "tinytable",    # Lightweight package to create tables
          "modelsummary", # Data and model summaries with tables and plots
          "pandoc",       # Required for saving tables as docx
          "hrbrthemes",   # Additional ggplot themes
          "extrafont",    # Additional fonts for plots etc
          "showtext",     # So that fonts also work on mac
          "patchwork"     # Combine ggplot objects
          )

groundhog.library(pkgs, "2024-07-01") 

Data Preparations

Add a new subj_idx to data_raw.

id_order <- unique(data_raw$`Participant Private ID`)

data_raw <- data_raw %>%
  mutate(subj_idx = match(`Participant Private ID`, id_order))

id_table <- data_raw %>%
  select(`Participant Private ID`, subj_idx) %>%
  distinct() 

# save the id table, so that we can use the same matches for the fake news task
id_dir <- here("01_data", "private_id_to_subj_idx.csv")
write_csv(id_table, id_dir, 
          na = "", append = FALSE, col_names = TRUE)

Create numerical versions of the variables response and stimulus.

A go response is 1, and a no-go response is 0.

data_num <- data_raw %>% 
  select(subj_idx, 
         `Participant Private ID`:condition) %>% 
  rename(response_lab = response,
         stimulus_lab = stimulus) %>% 
  mutate(response = if_else(response_lab == "nogo", 0, 1),
         stimulus = if_else(stimulus_lab == "nogo", 0, 1))

Also, the rt for no-go trials and missed go trials should be NA, and not 500 as it is now.

data_num <- data_num %>% 
  mutate(rt = if_else(response == 0, NA_real_, rt))

Filter data

Remove practice trials

initial_rows <- nrow(data_num)
data_filtered <- data_num %>% 
  filter(!str_detect(trial_id, "^practice_"))
removed_practice <- initial_rows - nrow(data_filtered)

print(paste(removed_practice, "practice trials have been removed"))
[1] "10080 practice trials have been removed"

Remove outliers

Responses with reaction times < 150 ms will be removed, as such fast reaction times are very unlikely to be actual responses and not just guesses.

outlier_num <- data_filtered %>% 
  filter(rt < 150) %>% 
  nrow() 

outlier_per <- (outlier_num / nrow(data_filtered)) * 100

print(paste("There are", outlier_num, "trials with a rt < 150 ms, which is", round(outlier_per, 2), "% of the total number of trials =", nrow(data_num)))
[1] "There are 1344 trials with a rt < 150 ms, which is 0.95 % of the total number of trials = 151200"
data_filtered <- data_filtered %>% 
  filter(is.na(rt) | rt >= 150)

Accuracy, Commission Errors, Omission Errors

gng_performance <- data_filtered %>%
  group_by(subj_idx) %>%
  summarise(
    commission_errors = sum(stimulus == 0 & response == 1, na.rm = TRUE),
    omission_errors = sum(stimulus == 1 & response == 0, na.rm = TRUE),
    go_accuracy = sum(stimulus == 1 & response == 1, na.rm = TRUE) / sum(stimulus == 1, na.rm = TRUE) * 100,
    nogo_accuracy = sum(stimulus == 0 & response == 0, na.rm = TRUE) / sum(stimulus == 0, na.rm = TRUE) * 100,
    overall_accuracy = sum(stimulus == response, na.rm = TRUE) / n() * 100,
    rt_go_avg = mean(rt[stimulus == 1 & !is.na(rt)]),
    rt_nogo_avg = mean(rt[stimulus == 0 & !is.na(rt)]),
  )

Reverse-coded commission errors

Add reverse-coded commission errors (this was not pre-registered, but to make interpretations more intuitive reverse-coding the commission errors will make it directionally aligned with the CRT correct score –> higher values indicate higher cognitive control)

gng_performance <- gng_performance %>% 
  mutate(commission_errors_r = 56 - commission_errors)

Descriptive checks

How many and which participants have an accuracy below 50%?

gng_performance %>% 
  filter(overall_accuracy <= 50) %>% 
  tt()
tinytable_0cd48fpwsovgd5fuxtv5
subj_idx commission_errors omission_errors go_accuracy nogo_accuracy overall_accuracy rt_go_avg rt_nogo_avg commission_errors_r
255 0 224 0.0 100.0 20.0 NaN NaN 56
269 0 224 0.0 100.0 20.0 NaN NaN 56
413 0 224 0.0 100.0 20.0 NaN NaN 56
445 15 123 43.3 71.7 48.9 303 249 41
gng_performance %>% 
  filter(overall_accuracy <= 50) %>% 
  tt() %>% 
  save_tt(here("03_output", "tables", "gng_fails.docx"), 
          overwrite = TRUE)
gng_performance %>% 
  select(commission_errors, commission_errors_r, omission_errors:rt_nogo_avg) %>% 
  datasummary_skim()
tinytable_df9q7m8yxxtot8gjzx2g
Unique Missing Pct. Mean SD Min Median Max Histogram
commission_errors 42 0 18.5 8.4 0.0 18.0 43.0
commission_errors_r 42 0 37.5 8.4 13.0 38.0 56.0
omission_errors 48 0 10.1 20.8 0.0 6.0 224.0
go_accuracy 164 0 95.4 9.4 0.0 97.3 100.0
nogo_accuracy 129 0 66.9 15.4 20.4 68.4 100.0
overall_accuracy 212 0 89.6 8.1 20.0 91.1 99.3
rt_go_avg 502 1 327.3 27.6 251.4 328.0 416.8
rt_nogo_avg 502 1 292.5 28.4 218.6 290.9 411.7
# and save it
gng_performance %>% 
  select(commission_errors:rt_nogo_avg) %>% 
  datasummary_skim(fun_numeric = list(Mean = Mean, 
                                      SD = SD, Min = Min, 
                                      Median = Median, 
                                      Max = Max
                                      )) %>% 
  save_tt(here("03_output", "tables", "gng_performance.docx"), overwrite = TRUE)

Plot the rt distribution during Go trials.

data_filtered %>%
  filter(stimulus == 1 & !is.na(rt)) %>% 
  slice(1:3500) %>% 
  ggplot(aes(x = rt)) +
  geom_density(alpha = 0.6, color = bp[5], fill = bp[6]) + 
  facet_wrap(~ subj_idx, ncol = 4) +  
  labs(x = "Reaction Time (ms)", y = element_blank(),
       title = "Distribution of Reaction Times during Go Trials for Different Participant") +
  guides(color = "none", alpha = "none", fill = "none") +
  theme_ipsum_rc(plot_title_size = 14, grid = F) + 
  theme(legend.position = "bottom",
        legend.title = element_blank(),
        strip.text = element_text(size = 10))

ggsave(here("03_output", "figures", "gng_sbj_rt_dist.png"), width = 9, height = 6, dpi = 300)

Plot the overall distribution of commission errors

gng_performance %>% 
  ggplot(aes(x = commission_errors_r)) +
  geom_histogram(binwidth = 1, fill = "steelblue", color = "white") +
  theme_ipsum_rc()+
  labs(
    title = "Distribution of Reverse-scored Commission Errors",
    x = "Commission Errors",
    y = "Count"
  )

Reorder variables

gng_performance <- gng_performance %>% 
  select(subj_idx, commission_errors, commission_errors_r, omission_errors:rt_nogo_avg)

Save data frames

# sum scores
write_csv(gng_performance, here("01_data", "scored", "data_gng_scores.csv"), 
          na = "", append = FALSE, col_names = TRUE)

# gng data
write_csv(data_filtered, here("01_data", "cleaned", "data_gng_filtered.csv"), 
          na = "", append = FALSE, col_names = TRUE)